/**
 * @file Fsim.cc
 * @author Xiaoze Lin (linxiaoze96@gmail.com)
 * @brief The top interface class of fault simulation.
 * @version 0.1
 *
 * @copyright Copyright (c) 2024
 *
 */

#include "Fsim.hh"

namespace ifsim {

Fsim* Fsim::_fsim = nullptr;

Fsim::Fsim()
{
  _atpgLib = new AtpgLibrary();
  _faultManager = new FaultManager(_atpgLib);
  _funcPatternManager = new FuncPatternManager();
  _simulation = new Simulation(_faultManager, _funcPatternManager);
}

Fsim::~Fsim() = default;

/**
 * @brief: Get the top atpg instance, if not, create one.
 * @return Fsim*
 */
Fsim* Fsim::getOrCreateFsim()
{
  if (_fsim == nullptr) {
    _fsim = new Fsim();
  }
  return _fsim;
}

/**
 * @brief: Destroy the atpg.
 */
void Fsim::destroyFsim()
{
  delete _fsim;
  _fsim = nullptr;
}

/**
 * @brief: Read the verilog file.
 */
void Fsim::readVerilog(const char* verilog_file)
{
  // ieda::Stats stats;
  LOG_INFO << "read verilog file " << verilog_file << " start";

  _netlistManager = NetlistManager::getOrCreateNetlistManager();
  bool is_ok = _netlistManager->readVerilog(verilog_file);

  LOG_FATAL_IF(!is_ok) << "read verilog file " << verilog_file << " failed.";
  LOG_INFO << "read verilog end";

  // dump memory and time message
  // double memory_delta = stats.memoryDelta();
  // LOG_INFO << "memory usage " << memory_delta << "MB";
  // double time_delta = stats.elapsedRunTime();
  // LOG_INFO << "time elapsed " << time_delta << "s";
}

/**
 * @brief: Read the library file.
 */
void Fsim::readLibrary(const char* library_file)
{
  LOG_INFO << "read library file " << library_file << " start";

  _atpgLib->parserStdCells(library_file);

  LOG_INFO << "read library file end";
}

/**
 * @brief: Set circuit handlings.
 */
void Fsim::setCircuitHandling(bool split_tie_flag, int max_fanout, bool no_dff_buffers_flag)
{
  _netlistManager = NetlistManager::getOrCreateNetlistManager();
  _netlistManager->splitTieNodes(split_tie_flag);
  _netlistManager->maxFanout(max_fanout);
  _netlistManager->dffBuffers(!no_dff_buffers_flag);
}

/**
 * @brief: Set circuit handlings.
 */
void Fsim::setCircuitHandling()
{
  bool split_tie_flag = true;
  int max_fanout = 4;
  bool no_dff_buffers_flag = false;
  _netlistManager = NetlistManager::getOrCreateNetlistManager();
  _netlistManager->splitTieNodes(split_tie_flag);
  _netlistManager->maxFanout(max_fanout);
  _netlistManager->dffBuffers(!no_dff_buffers_flag);
}

/**
 * @brief: Generate and process netlists.
 */
void Fsim::genAndProcessNetlists()
{
  LOG_INFO << "gen and process netlists start";

  _netlistManager = NetlistManager::getOrCreateNetlistManager();

  _netlistManager->verilogToStdCellNetlist(_topModuleName, _atpgLib);

  _netlistManager->stdCellNetlistToPrimitiveNetlist(_atpgLib);
  _netlistManager->genLevelizedNetlist(false);  // 'false' means not print debug info
  _netlistManager->simNetlist().simToFindTieGates();

  LOG_INFO << "gen and process netlists end";
}

/**
 * @brief: Create fault.
 */
void Fsim::createFault()
{
  LOG_INFO << "create fault start";

  _faultManager->identifyFullAndDifferentClassFaults();

  LOG_INFO << "create fault end";
}

/**
 * @brief: Read the fault list file.
 */
void Fsim::readFaultList(const char* fault_list_file)
{
  LOG_INFO << "read fault list file " << fault_list_file << " start";

  bool is_ok;
  is_ok = _faultManager->readFaultList(fault_list_file);
  LOG_FATAL_IF(!is_ok) << "read fault list file " << fault_list_file << " failed.";

  LOG_INFO << "read fault list file end";
}

/**
 * @brief: Read the external pattern file.
 */
void Fsim::readPattern(std::string pattern_file)
{
  // ieda::Stats stats;

  if (pattern_file.substr(pattern_file.find_last_of(".") + 1) == "vcd") {  // read from specified pattern file
    LOG_INFO << "read func pattern file " << pattern_file << " start";

    if (!_funcPatternManager->readFuncPattern(pattern_file)) {
      LOG_ERROR << "read func pattern file fail";
    }

    LOG_INFO << "read func pattern file " << pattern_file << " end";

  } else {
    std::cout << ErrColor << "no pattern file is specified" << NormalColor << std::endl;
  }

  // dump memory and time message
  // double memory_delta = stats.memoryDelta();
  // LOG_INFO << "memory usage " << memory_delta << "MB";
  // double time_delta = stats.elapsedRunTime();
  // LOG_INFO << "time elapsed " << time_delta << "s";
}

/**
 * @brief: Logic simluation.
 */
void Fsim::simulatePatterns(int num_thread, bool is_bit_parallel, bool is_vec_mode)
{
  if (_faultManager->fullFaultList().empty() && _faultManager->readFaultList().empty()) {  // logic simulation
    LOG_INFO << "logic simulation with functional pattern start";

    bool vec_mode = false;
    LOG_INFO << "num of func pat frame: " << _funcPatternManager->frameNum();
    bool is_ok = _simulation->simGoodCktWithFuncPat(vec_mode);
    LOG_FATAL_IF(!is_ok) << "simulate functional patterns failed.";

    LOG_INFO << "logic simulation end";

  } else {  // fault simulation
    if (is_vec_mode) {
      LOG_INFO << "fault simulation with functional pattern in vecter mode start, num thread: " << num_thread;
    } else {
      LOG_INFO << "fault simulation with functional pattern in scalar mode start, num thread: " << num_thread;
    }
    LOG_INFO << "num of func pat frame: " << _funcPatternManager->frameNum();

    bool is_ok = _simulation->faultSimulate(num_thread, is_bit_parallel, is_vec_mode);
    LOG_FATAL_IF(!is_ok) << "fault simulation failed.";

    LOG_INFO << "fault simulation end";
  }
}
}  // namespace ifsim